package com.price.common.util

import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.graphics.Rect
import android.os.Build
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.view.Window
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import com.price.common.extend.navigationBarHeight

class KeyboardUtils : OnGlobalLayoutListener {
    private var mCallback: SoftKeyboardToggleListener?
    private var mRootView: ViewGroup
    private var prevValue: Boolean? = null

    interface SoftKeyboardToggleListener {
        /**
         * 键盘显示状态监听回调
         *
         * @param isVisible 键盘是否显示
         */
        fun onToggleSoftKeyboard(isVisible: Boolean)
    }

    override fun onGlobalLayout() {
        val isVisible = isSoftInputShow(mRootView)
        if (mCallback != null && (prevValue == null || isVisible != prevValue)) {
            prevValue = isVisible
            mCallback!!.onToggleSoftKeyboard(isVisible)
        }
    }

    private fun removeListener() {
        mCallback = null
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mRootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
        } else {
            mRootView.viewTreeObserver.removeGlobalOnLayoutListener(this)
        }
    }

    private constructor(activity: Activity, listener: SoftKeyboardToggleListener) {
        mCallback = listener
        mRootView = activity.window.decorView as ViewGroup
        mRootView.viewTreeObserver.addOnGlobalLayoutListener(this)
    }

    private constructor(viewGroup: ViewGroup, listener: SoftKeyboardToggleListener) {
        mCallback = listener
        mRootView = viewGroup
        mRootView.viewTreeObserver.addOnGlobalLayoutListener(this)
    }

    companion object {
        private val sListenerMap = HashMap<SoftKeyboardToggleListener, KeyboardUtils>()

        /**
         * Add a new keyboard listener
         *
         * @param act      calling activity
         * @param listener callback
         */
        fun addKeyboardToggleListener(act: Activity, listener: SoftKeyboardToggleListener) {
            removeKeyboardToggleListener(listener)
            sListenerMap[listener] = KeyboardUtils(act, listener)
        }

        /**
         * Add a new keyboard listener
         *
         * @param act      calling activity
         * @param listener callback
         */
        fun addKeyboardToggleListener(act: ViewGroup, listener: SoftKeyboardToggleListener) {
            removeKeyboardToggleListener(listener)
            sListenerMap[listener] = KeyboardUtils(act, listener)
        }

        /**
         * Remove a registered listener
         *
         * @param listener [SoftKeyboardToggleListener]
         */
        fun removeKeyboardToggleListener(listener: SoftKeyboardToggleListener) {
            if (sListenerMap.containsKey(listener)) {
                val k = sListenerMap[listener]
                k?.removeListener()
                sListenerMap.remove(listener)
            }
        }

        /**
         * Remove all registered keyboard listeners
         */
        fun removeAllKeyboardToggleListeners() {
            for (l: SoftKeyboardToggleListener in sListenerMap.keys) {
                val k = sListenerMap[l]
                k?.removeListener()
            }
            sListenerMap.clear()
        }

        /**
         * Manually toggle soft keyboard visibility
         *
         * @param context calling context
         */
        fun toggleKeyboardVisibility(context: Context) {
            val inputMethodManager =
                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
        }

        /**
         * Force closes the soft keyboard
         *
         * @param activeView the view with the keyboard focus
         */
        fun forceCloseKeyboard(activeView: View) {
            val inputMethodManager =
                activeView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            inputMethodManager.hideSoftInputFromWindow(activeView.windowToken, 0)
        }

        /**
         * 软键盘以覆盖当前界面的形式出现
         *
         * @param activity
         */
        fun setSoftInputAdjustNothing(activity: Activity) {
            activity.window.setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
                        or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
            )
        }

        /**
         * 软键盘以顶起当前界面的形式出现, 注意这种方式会使得当前布局的高度发生变化，触发当前布局onSizeChanged方法回调，这里前后高度差就是软键盘的高度了
         *
         * @param activity
         */
        fun setSoftInputAdjustResize(activity: Activity) {
            activity.window.setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                        or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
            )
        }

        /**
         * 软键盘以上推当前界面的形式出现, 注意这种方式不会改变布局的高度
         *
         * @param activity
         */
        fun setSoftInputAdjustPan(activity: Activity) {
            activity.window.setSoftInputMode(
                (WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
                        or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
            )
        }

        /**
         * 输入键盘是否在显示
         *
         * @param activity 应用窗口
         */
        fun isSoftInputShow(activity: Activity?): Boolean {
            return activity != null && isSoftInputShow(activity.window)
        }

        /**
         * 输入键盘是否在显示
         *
         * @param window 应用窗口
         */
        fun isSoftInputShow(window: Window?): Boolean {
            return if (window != null && window.decorView is ViewGroup) {
                isSoftInputShow(window.decorView as ViewGroup)
            } else false
        }

        /**
         * 输入键盘是否在显示
         *
         * @param rootView 根布局
         */
        fun isSoftInputShow(rootView: ViewGroup?): Boolean {
            if (rootView == null) {
                return false
            }
            val viewHeight = rootView.height
            //获取View可见区域的bottom
            val rect = Rect()
            rootView.getWindowVisibleDisplayFrame(rect)
            val space = viewHeight - rect.bottom - navigationBarHeight
            return space > 0
        }

        /**
         * 禁用物理返回键
         *
         *
         * 使用方法：
         *
         * 需重写 onKeyDown
         *
         * @param keyCode
         * @return 是否拦截事件
         *
         *
         * @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
         * return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event);
         * }
         *
         */
        fun onDisableBackKeyDown(keyCode: Int): Boolean {
            when (keyCode) {
                KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_HOME -> return false
                else -> {}
            }
            return true
        }

        /**
         * 点击屏幕空白区域隐藏软键盘
         *
         * 根据 EditText 所在坐标和用户点击的坐标相对比，来判断是否隐藏键盘
         *
         * 需重写 dispatchTouchEvent
         *
         * @param ev       点击事件
         * @param activity 窗口
         */
        fun dispatchTouchEvent(ev: MotionEvent?, activity: Activity) {
            dispatchTouchEvent(ev, activity.window)
        }

        /**
         * 点击屏幕空白区域隐藏软键盘
         *
         * 根据 EditText 所在坐标和用户点击的坐标相对比，来判断是否隐藏键盘
         *
         * 需重写 dispatchTouchEvent
         *
         * @param ev     点击事件
         * @param dialog 窗口
         */
        fun dispatchTouchEvent(ev: MotionEvent?, dialog: Dialog) {
            dispatchTouchEvent(ev, dialog.window)
        }

        /**
         * 点击屏幕空白区域隐藏软键盘
         *
         * 根据 EditText 所在坐标和用户点击的坐标相对比，来判断是否隐藏键盘
         *
         * 需重写 dispatchTouchEvent
         *
         * @param ev     点击事件
         * @param window 窗口
         */
        fun dispatchTouchEvent(ev: MotionEvent?, window: Window?) {
            if (ev == null || window == null) {
                return
            }
            if (ev.action == MotionEvent.ACTION_DOWN) {
                if (isShouldHideKeyboard(window, ev)) {
                    hideSoftInputClearFocus(window.currentFocus)
                }
            }
        }

        /**
         * 根据 EditText 所在坐标和用户点击的坐标相对比，来判断是否隐藏键盘
         *
         * @param view  窗口
         * @param event 用户点击事件
         * @return 是否隐藏键盘
         */
        fun isShouldHideKeyboard(view: View?, event: MotionEvent?): Boolean {
            return if ((view is EditText) && event != null) {
                !isTouchView(view, event)
            } else false
        }

        /**
         * 根据用户点击的坐标获取用户在窗口上触摸到的View，判断这个View是否是EditText来判断是否隐藏键盘
         *
         * @param window 窗口
         * @param event  用户点击事件
         * @return 是否隐藏键盘
         */
        fun isShouldHideKeyboard(window: Window?, event: MotionEvent?): Boolean {
            if (window == null || event == null) {
                return false
            }
            if (!isSoftInputShow(window)) {
                return false
            }
            if (window.currentFocus !is EditText) {
                return false
            }
            val decorView = window.decorView
            return if (decorView is ViewGroup) {
                findTouchEditText(decorView, event) == null
            } else false
        }

        private fun findTouchEditText(viewGroup: ViewGroup?, event: MotionEvent): View? {
            if (viewGroup == null) {
                return null
            }
            for (i in 0 until viewGroup.childCount) {
                val child = viewGroup.getChildAt(i)
                if (child == null || !child.isShown) {
                    continue
                }
                if (!isTouchView(child, event)) {
                    continue
                }
                if (child is EditText) {
                    return child
                } else if (child is ViewGroup) {
                    return findTouchEditText(child as ViewGroup?, event)
                }
            }
            return null
        }

        /**
         * 判断view是否在触摸区域内
         *
         * @param view  view
         * @param event 点击事件
         * @return view是否在触摸区域内
         */
        private fun isTouchView(view: View?, event: MotionEvent?): Boolean {
            if (view == null || event == null) {
                return false
            }
            val location = IntArray(2)
            view.getLocationOnScreen(location)
            val left = location[0]
            val top = location[1]
            val right = left + view.measuredWidth
            val bottom = top + view.measuredHeight
            return (event.y >= top) && (event.y <= bottom) && (event.x >= left
                    ) && (event.x <= right)
        }

        /**
         * 动态隐藏软键盘
         *
         * @param view 视图
         */
        fun hideSoftInput(view: View?) {
            if (view == null) {
                return
            }
            val imm: InputMethodManager =
                view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    ?: return
            imm.hideSoftInputFromWindow(view.windowToken, 0)
        }

        /**
         * 动态隐藏软键盘并且清除当前view的焦点【记住，要在xml的父布局加上android:focusable="true" 和 android:focusableInTouchMode="true"】
         *
         * @param view 视图
         */
        fun hideSoftInputClearFocus(view: View?) {
            if (view == null) {
                return
            }
            val imm: InputMethodManager =
                view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    ?: return
            imm.hideSoftInputFromWindow(view.windowToken, 0)
            (view as? EditText)?.clearFocus()
        }

        /**
         * 切换软键盘显示与否状态
         */
        fun toggleSoftInput(context: Context) {
            val imm: InputMethodManager =
                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
        }

        /**
         * 强制显示软键盘
         *
         * @param activity 活动窗口
         */
        fun showSoftInputForce(activity: Activity) {
            if (!isSoftInputShow(activity)) {
                toggleSoftInput(activity)
            }
        }

        /**
         * 显示软键盘
         *
         * @param view 可输入控件，并且在焦点上方可显示
         */
        fun showSoftInput(view: EditText?) {
            if (view == null) {
                return
            }
            val imm: InputMethodManager =
                view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    ?: return
            imm.showSoftInput(view, InputMethodManager.SHOW_FORCED)
        }

        /**
         * 修复软键盘内存泄漏
         *
         * @param context context
         */
        fun fixSoftInputLeaks(context: Context?) {
            if (context == null) {
                return
            }
            val imm: InputMethodManager =
                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    ?: return
            val strArr = arrayOf("mCurRootView", "mServedView", "mNextServedView")
            for (s: String? in strArr) {
                try {
                    val declaredField = imm.javaClass.getDeclaredField(s)
                    if (!declaredField.isAccessible) {
                        declaredField.isAccessible = true
                    }
                    val obj = declaredField[imm]
                    if (obj !is View) {
                        continue
                    }
                    if (obj.context === context) {
                        declaredField[imm] = null
                    } else {
                        return
                    }
                } catch (th: Throwable) {
                    th.printStackTrace()
                }
            }
        }
    }
}
